﻿/**
 * Copyright (C) 2011 original author and authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NGuice.Inject.Internal.Util
{
    /// <summary>
    /// 一个不可变的且不允许null元素的集合实现。
    /// <remarks>此类在外部包中不要被子类化，他不提供public或protected的构造函数。因此
    /// 此集合类型本身的实例也是不可变的
    /// </remarks>
    /// </summary>
    /// <typeparam name="T">集合中的元素类型</typeparam>
    [Serializable]
    public abstract class ImmutableCollection<T> : ICollection<T>
    {
        internal static readonly ImmutableCollection<T> EMPTY_IMMUTABLE_COLLECTION = new EmptyImmutableCollection<T>();

        public virtual void Add(T item)
        {
            throw new NotSupportedException();
        }

        public virtual void Clear()
        {
            throw new NotSupportedException();
        }

        public virtual  bool Contains(T item)
        {
            if (item == null)
            {
                return false;
            }
            foreach (T element in this)
            {
                if (element.Equals(item))
                {
                    return true;
                }
            }
            return false;
        }

        public virtual void CopyTo(T[] array, int arrayIndex)
        {
            T[] source = this.ToArray();
            Array.Copy(source, arrayIndex, array, arrayIndex, source.Length - arrayIndex - 1);
        }

        public abstract int Count
        {
            get;
        }

        public virtual bool IsReadOnly
        {
            get { return true; }
        }

        public virtual bool Remove(T item)
        {
            throw new NotSupportedException();
        }

        public virtual T[] ToArray()
        {
            Array newArray = Array.CreateInstance(typeof(T), Count);
            int index = 0;
            foreach (T element in this)
            {
                newArray.SetValue(element, index);
            }
            return ((IList<T>)newArray).ToArray();
        }

        public abstract IEnumerator<T> GetEnumerator();

        public virtual bool IsEmpty
        {
            get
            {
                return Count == 0;
            }
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder(Count * 16);
            sb.Append('[');
            IEnumerator<T> i = this.GetEnumerator();
            if (i.MoveNext())
            {
                sb.Append(i.Current);
            }
            while (i.MoveNext())
            {
                sb.Append(", ");
                sb.Append(i.Current);
            }
            return sb.Append(']').ToString();
        }

        private class EmptyImmutableCollection<E> : ImmutableCollection<E>
        {
            public override int Count
            {
                get
                {
                    return 0;
                }
            }

            private static readonly EmptyEnumerator<E> EMPTY_ENUMERATOR = new EmptyEnumerator<E>();

            private static readonly E[] EMPTY_ARRAY = new E[0];

            public override bool IsEmpty
            {
                get
                {
                    return true;
                }
            }

            public override bool Contains(E obj)
            {
                return false;
            }

            public override IEnumerator<E> GetEnumerator()
            {
                return EMPTY_ENUMERATOR;
            }

            public override E[] ToArray()
            {
                return EMPTY_ARRAY;
            }
        }

        internal sealed class EmptyEnumerator<E> : IEnumerator<E>
        {
            public E Current
            {
                get { throw new InvalidOperationException(); }
            }

            public void Dispose()
            {
            }

            public bool MoveNext()
            {
                return false;
            }

            public void Reset()
            {
            }

            object System.Collections.IEnumerator.Current
            {
                get { return Current; }
            }
        }

        private class ArrayImmutableCollection<E> : ImmutableCollection<E>
        {
            private readonly E[] elements;

            public ArrayImmutableCollection(E[] elements)
            {
                this.elements = elements;
            }

            public override int Count
            {
                get
                {
                    return elements.Length;
                }
            }

            public override bool IsEmpty
            {
                get
                {
                    return false;
                }
            }

            public override IEnumerator<E> GetEnumerator()
            {
                return elements.AsEnumerable().GetEnumerator();
            }
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }
}
